--[[ GEO SDK CORE :: STATE MODULE ]]--
--[[
	author:		genesis.erode / The Anger
	version:	5
	
	
---	EXPORTS ---
	
	
---	CLASSES ---
	
===	state/state ===
		
		the global G.state object.
		can be called via G.state() to return the state of the game / program; if true it should not end.
		
		
	***	bool	<state>( void )
		
		executes one cycle of the global state machine.
		returns true if the currently active <frame>, when triggered, returns true -- <state>[ <state>.frame ]()
		returns false if <state>[ <state>.frame ] is not a <gframe>.
		
		
	***	<frame>		<state>:newFrame()
		
		creates a <frame> object, returns a reference to it and parents it to the calling <state>.
		
		
	+++	property seq	<state>.frameid = 1
		
		matches the <frame>.frameid property of the currently active <gframe>.
		
		
		
===	state/Ent ===
		
		<Ent>.state = state property of all Entities (each entity has its own instance)
		
		
	+++	property seq	<state/Ent>.frameid = 0
		
		the currently active <frame>.
		a value of 0 indicates the FSM is zombified (does nothing & returns false).
		to unzombify the FSM, a child frame needs to be activated.
		
		
	+++	property <Ent>	<state/Ent>.ent
		
		returns a reference to the parent entity, if one exists.
		
		
	***	bool	<state/Ent>( void )
		
		executes one cycle of the entity's state machine.
		returns true if the current state returned true.
		if no state can be found, returns false.
		if the return value is false, it also means the state machine zombified.
		a zombified state means this function will continue to return false without further execution.
		to unzombify the FSM, a child frame needs to be activated.
		
		
	***	<frame>		<state/Ent>:newFrame()
		
		creates a <frame> object, returns a reference to it and parents it to the calling <state/Ent>.
		
		
		
===	state/frame ===
		
		a state frame = a peice of logic / functor, working as one of a collection of states in a FSM.
		
		
	***	bool	<frame>( void )
		
		executes one cycle of the frame's step function, if it exists, and returns the value.
		return value from step() is converted to bool.
		if step is not a function, returns false.
		
		
	***	void	<frame>:activate()
		
		activates the frame so that the next cycle, this frame is used as the state action.
		must be parented in order for this to succeed, otherwise nothing happens.
		
		
	***	seq		<frame>:frameid( void )
		
		= sibling position within its parent, if parented (which it should be usually).
		
		by definition:
		<frame> = <frame>._parent[ <frame>:frameid() ]
		
		
	***	virtual bool	<frame>:step( void )
		
		by default, always returns true.
		override with another equivalent function (must actually be a function) to insert state frame code.
		
]]--
do	local _mod_name = [=========================[state]=========================]
	
	local G_core_is_gstype = G.core.is_gstype
	local cls_state, cls_frame, cls_ent = 'state/state','state/frame','state/Ent'
	
	G.core.mods[ _mod_name ] = {
		[ _mod_name ] = G.core.Obj_indexhelper (
			-- metatable , statics , defaults --
			{
				__call = function(this)
					local f = this[this.frameid]
					return G.core.is_gstype(f, cls_frame) and f()
				end
			},{
				[ [====[newFrame]====] ] =
				function(this, defaults)
					return G.core.moveto( G.core.newObjEx( defaults, cls_frame ), this )
				end
			},{
				[ [====[frameid]====] ] = 1
			}
		),
		
		[ [================[frame]================] ] = G.core.Obj_indexhelper (
			-- metatable , statics , defaults --
			{
				__call = function(this)
					local f = this.step
					return type(f)=='function' and f(this) and true or false
				end
			},{
				[ [====[frameid]====] ] =
				G.core.get_seq;
				
				[ [====[activate]====] ] =
				function(this)
					local id = this:frameid()
					if id then this._parent.frameid = id end
				end
			},{
				[ [====[step]====] ] =
				function()return true end
			}
		),
		
		[ [================[Ent]================] ] = G.core.Obj_indexhelper (
			-- metatable , statics , defaults --
			{
				__call = function(this)
					local f = this.frameid
					if f == 0 then return false end
					local frame = this[f]
					if G_core_is_gstype(frame, cls_frame) and frame() then return true end
					this.frameid = 0
					return false
				end
			},{
				[ [====[newFrame]====] ] =
				function(this, defaults)
					return G.core.moveto( G.core.newObjEx( defaults, cls_frame ), this )
				end
			},{
				[ [====[frameid]====] ] = 0
			}
		)
	}
	
end
